package com.xhga.transaction.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xhga.transaction.common.redis.RedisApi;
import com.xhga.transaction.common.redis.RedisConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 生成唯一标识
 * Created by HuaWeiBo on 2019/4/18.
 */
public class UniqueUtil {

    private static final Logger logger = LoggerFactory.getLogger(UniqueUtil.class);

    private static Map<String, Map<String, Queue<String>>> map = new ConcurrentHashMap<>();

    private RedisApi redisApi;

    public UniqueUtil(RedisApi redisApi) {
        this.redisApi = redisApi;
    }

    public static synchronized String getCentreNo(String type, String[] serverNames,
                                     int count, String serverName) {
        String no = UniqueUtil.timeMillisAndRandom();
        if (map.containsKey(type)){
            Map<String, Queue<String>> queueMap = map.get(type);
            if (serverName.equals(serverNames[0])) {
                for (Map.Entry<String, Queue<String>> entry : queueMap.entrySet()) {
                    Queue<String> queue = entry.getValue();
                    queue.offer(no);
                }
            }
            Queue<String> queue = queueMap.get(serverName);
            no = queue.poll();
        } else {
            Map<String, Queue<String>> queueMap = new HashMap<>(count);
            for (int i = 0; i < count; i++) {
                Queue<String> queue = new LinkedList<>();
                queue.offer(no);
                queueMap.put(serverNames[i], queue);
            }
            Queue<String> queue = queueMap.get(serverName);
            no = queue.poll();
            map.put(type, queueMap);
        }
        logger.info("获得No:{},map:{}", no, map);
        return no;
    }

    public String getRedisCentreNo(String type, String[] serverNames,
                                   int count, String serverName) {
        return distributedLocks(type, serverNames, count, serverName);
    }


    /**
     * setNx 初略实现分布式锁(不可靠)
     * @param type
     * @param serverNames
     * @param count
     * @param serverName
     * @return
     */
    public String distributedLocks(String type, String[] serverNames,
                                   int count, String serverName) {
        redisApi.lock(RedisConfig.LOCK_GET_NO + type, serverName);
        // serverName在serverNames的坐标
        int index = 0;
        for (int i = 0; i < serverNames.length; i++) {
            if (serverName.equals(serverNames[i])) {
                index = i;
                break;
            }
        }
        String no = UniqueUtil.timeMillisAndRandom();
        List<String> queues = redisApi.getList(RedisConfig.TRANSACTION_TYPE, type, serverName);
        String typeConfig = RedisConfig.TRANSACTION_TYPE + type;
        // 初始化redis
        if (queues == null || queues.size() ==0) {
            for (int i = 0; i < count; i++) {
                Queue<String> queue = new LinkedList<>();
                if (index != i) {
                    queue.offer(no);
                }
                String jsonString = JSONObject.toJSONString(queue);
                redisApi.addList(typeConfig, jsonString);
            }
        } else {
            // 是否增加队列值
            if (JSON.parseArray(queues.get(0)).size() < 5) {
                String newNo = no;
                for (int i = 0; i < count; i++) {
                    JSONArray jsonArray = JSON.parseArray(queues.get(i));
                    Queue<String> queue = new LinkedList(jsonArray);
                    queue.offer(newNo);
                    if (index == i) {
                        no = queue.poll();
                    }
                    redisApi.setList(typeConfig, i, JSONObject.toJSONString(queue));
                }
            } else {
                JSONArray jsonArray = JSON.parseArray(queues.get(index));
                Queue<String> queue = new LinkedList(jsonArray);
                no = queue.poll();
                redisApi.setList(typeConfig, index, JSONObject.toJSONString(queue));
            }
        }
        redisApi.unLock(RedisConfig.LOCK_GET_NO + type);
        logger.info("JDBC获得No:{},map:{}", no, queues);
        return no;
    }

    public static void updateCentreNo(String type, String centreNo) {
        Map<String, Queue<String>> queueMap = map.get(type);
        for (Map.Entry<String, Queue<String>> entry : queueMap.entrySet()) {
            Queue<String> queue = entry.getValue();
            queue.remove(centreNo);
        }
    }

    public void updateRedisCentreNo(String type, String centreNo, int count) {
        type = RedisConfig.TRANSACTION_TYPE + type;
        List<String> queueMap = redisApi.getList(type);
        for (int i = 0; i < count; i++) {
            JSONArray jsonArray = JSON.parseArray(queueMap.get(i));
            Queue<String> queue = new LinkedList(jsonArray);
            if (queue.remove(centreNo)) {
                redisApi.setList(type, i, JSONObject.toJSONString(queue));
            }
        }
    }

    public static String uuid() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString();
    }

    /**
     * 时间戳 + 四位随机数
     * @return
     */
    public static String timeMillisAndRandom() {
        Random random = new Random();
        long no = System.currentTimeMillis() * 10000 + random.nextInt(10000);
        return String.valueOf(no);
    }

    public static void main(String[] args) {
        String[] names = new String[]{"a","b","c"};
        System.out.println("a" + UniqueUtil.getCentreNo("type",names,2,"a"));
        System.out.println("b" + UniqueUtil.getCentreNo("type",names,2,"b"));
        String centreNo = UniqueUtil.getCentreNo("type", names, 2, "a");
        System.out.println("a" + centreNo);
        UniqueUtil.updateCentreNo("type",centreNo);
        System.out.println("a" + UniqueUtil.getCentreNo("type",names,2,"a"));
        System.out.println("b" + UniqueUtil.getCentreNo("type",names,2,"b"));
//        System.out.println("b" + UniqueUtil.getCentreNo("type",names,3,"b"));
//        System.out.println("b" + UniqueUtil.getCentreNo("type",names,3,"b"));
//        System.out.println("c" + UniqueUtil.getCentreNo("type",names,3,"c"));
    }

}
